package model;


import java.awt.Image;
import java.awt.Point;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Vector;
import javax.imageio.ImageIO;
import various.Constants;

/**
 * La classe ModelTileCK correspond a la representation d'une tuile. Ainsi nous avons definit une tuile comme ayant 12 contraintes.
 * Une contrainte correspondant a une chemin, ville, champs. 
 * Une tuile contiendra aussi un tableau de voisins, au maximum de 4, chaque voisin �tant reli� par 3 contraintes.
	public abstract void rotate(); 
 * @author kn -cm
 *
 */
public class ModelTileCK extends ModelTile{

	private int oldAngle;
	private int newAngle;
	private boolean symbol;
	private Image picture;
	private final int SIDENUMBER = 4;
	private final String link;
	public ModelTileCK(ModelConstraint constraintsList[],ModelTile[] nghbrs,boolean symbol, String link) throws IOException
	{
		this.oldAngle=0;
		this.newAngle=0;
		super.constraints = constraintsList;

		if(nghbrs == null){
			super.neighbours = new ModelTileCK[SIDENUMBER];
			for(int i=0; i<super.neighbours.length; i++){
				super.neighbours[i] = null;
			}
		}
		else
			super.neighbours = nghbrs;
		super.positions = new Vector<ModelPosition>();
		this.link=link;
		this.symbol = symbol;
		this.picture = ImageIO.read(new File(this.link));

	}

	public int getOldAngle() {
		return oldAngle;
	}

	public void setOldAngle(int angle) {
		this.oldAngle = angle;
	}

	public void setPosition(Vector<ModelPosition> vmp){
		super.positions = vmp;
	}

	public int getNewAngle() {
		return newAngle;
	}

	public void setNewAngle(int newAngle) {
		this.newAngle = newAngle;
	}

	public Vector<ModelPosition> getPosition(){
		return super.positions;
	}

	public ModelTileCK(ModelConstraint constraintsList[],boolean symbol, String link) throws IOException
	{
		//liste de voisins nuls
		this(constraintsList,null,symbol, link);
	}

	public ModelConstraint getNorthConstraints()
	{
		return (ModelConstraint)super.constraints[Constants.NORTH];
	}

	public ModelConstraint[] getNorthConstraintsFull()
	{
		ModelConstraint tab []={super.constraints[Constants.NORTH -1 ],super.constraints[Constants.NORTH ],super.constraints[Constants.NORTH+1]};
		return tab;
	}

	public void setCstNFull(ModelConstraint[] mc)
	{
		this.getNorthConstraintsFull()[0] = mc[0];
		this.getNorthConstraintsFull()[1] = mc[1];
		this.getNorthConstraintsFull()[2] = mc[2];
	}

	public void setCstEFull(ModelConstraint[] mc)
	{
		this.getEastConstraintsFull()[0] = mc[0];
		this.getEastConstraintsFull()[1] = mc[1];
		this.getEastConstraintsFull()[2] = mc[2];
	}

	public void setCstSFull(ModelConstraint[] mc)
	{
		this.getSouthConstraintsFull()[0] = mc[0];
		this.getSouthConstraintsFull()[1] = mc[1];
		this.getSouthConstraintsFull()[2] = mc[2];
	}

	public void setCstWFull(ModelConstraint[] mc)
	{
		this.getWestConstraintsFull()[0] = mc[0];
		this.getWestConstraintsFull()[1] = mc[1];
		this.getWestConstraintsFull()[2] = mc[2];
	}

	public ModelConstraint getEastConstraints()
	{
		return (ModelConstraint)super.constraints[Constants.EAST];
	}

	public ModelConstraint[] getEastConstraintsFull()
	{
		ModelConstraint tab []={super.constraints[Constants.EAST -1 ],super.constraints[Constants.EAST ],super.constraints[Constants.EAST+1]};
		return tab;
	}


	public ModelConstraint getSouthConstraints()
	{
		return (ModelConstraint)super.constraints[Constants.SOUTH];
	}

	public ModelConstraint[] getSouthConstraintsFull()
	{
		ModelConstraint tab []={super.constraints[Constants.SOUTH-1 ],super.constraints[Constants.SOUTH ],super.constraints[Constants.SOUTH+1]};
		return tab;	
	}

	public ModelConstraint getWestConstraints()
	{
		return (ModelConstraint)super.constraints[Constants.WEST];
	}

	public ModelConstraint[] getWestConstraintsFull()
	{
		ModelConstraint tab []={super.constraints[Constants.WEST-1 ],super.constraints[Constants.WEST ],super.constraints[Constants.WEST+1]};
		return tab;	
	}


	//retourne les positons libres cad ou il n'y pa de pions
	public Vector<Point> getFreePositions()
	{
		Vector<Point> p = new Vector<Point>();
		for(int i =0;i< super.positions.size() ; i++)
		{
			if(super.positions.get(i).getPawn() == null)
			{
				p.add(super.positions.get(i).getPoint());
			}
		}
		if (p.isEmpty())
			return null;
		else
			return p;
	}

	public int getPawnIndex()
	{
		int id = -1;
		for(int i=0; i<super.positions.size();i++){
			if(super.positions.get(i).getPawn() != null)
				id= i;
		}
		return id;
	}

	/**
	 * Cette fonction est importante pour la rotation, lorsqu'une tuile es tournee, il faut reattribuer les contraintes au bon endroit
	 * Pareillement pour les points qui correspondent aux emplacement possible des pions.
	 */
	public void rotate() {
		ModelConstraint tempN0 = null, tempN1 = null, tempN2 = null,
		tempE0 = null,tempE1 = null,tempE2 = null,
		tempS0 = null,tempS1 = null,tempS2 = null;

		//rotation de 90
		//les valeurs de north vont dans east
		//les valeurs de east vont  dans south
		//les valeurs de south vont dans west
		//les valeurs de west devient north

		tempN0 = super.constraints[Constants.NORTH-1];
		tempN1 = super.constraints[Constants.NORTH];
		tempN2 =  super.constraints[Constants.NORTH+1];
		super.constraints[Constants.NORTH -1] = super.constraints[Constants.WEST-1];
		super.constraints[Constants.NORTH] = super.constraints[Constants.WEST];
		super.constraints[Constants.NORTH+1] = super.constraints[Constants.WEST+1];

		tempE0 = super.constraints[Constants.EAST-1];
		tempE1 = super.constraints[Constants.EAST];
		tempE2 = super.constraints[Constants.EAST+1];
		super.constraints[Constants.EAST-1] = tempN0;
		super.constraints[Constants.EAST] = tempN1;
		super.constraints[Constants.EAST+1] = tempN2;

		tempS0 = super.constraints[Constants.SOUTH-1];
		tempS1 = super.constraints[Constants.SOUTH];
		tempS2 = super.constraints[Constants.SOUTH+1];
		super.constraints[Constants.SOUTH-1] = tempE0;
		super.constraints[Constants.SOUTH] = tempE1;
		super.constraints[Constants.SOUTH+1] = tempE2;

		super.constraints[Constants.WEST-1] = tempS0;
		super.constraints[Constants.WEST] = tempS1;
		super.constraints[Constants.WEST+1] = tempS2;

		//on met a jour le nouvel angle
		this.newAngle += 90;
		
		// on met a jour les positions
        int x, y;
        for(int i=0; i<super.positions.size(); i++)
        {
            x = (int) super.positions.get(i).getPoint().getX();
            y = (int) super.positions.get(i).getPoint().getY();
            if((x < (Constants.sizeSquare/2)) && (y < (Constants.sizeSquare/2)))
                super.positions.get(i).getPoint().setLocation(Constants.sizeSquare-x, y);
           
            if((x > (Constants.sizeSquare/2)) && (y < (Constants.sizeSquare/2)))
                super.positions.get(i).getPoint().setLocation(x, Constants.sizeSquare-y);
           
            if((x < (Constants.sizeSquare/2)) && (y > (Constants.sizeSquare/2)))
                super.positions.get(i).getPoint().setLocation(Constants.sizeSquare-x, Constants.sizeSquare-y);
           
            if((x > (Constants.sizeSquare/2)) && (y > (Constants.sizeSquare/2)))
                super.positions.get(i).getPoint().setLocation(Constants.sizeSquare-x, y);
        }
	}


	public void setNorthNeighbours(ModelTileCK ck)
	{
		this.neighbours[0] = ck;
	}

	public void setEastNeighbours(ModelTileCK ck)
	{
		this.neighbours[1] = ck;
	} 

	public void setSouthNeighbours(ModelTileCK ck)
	{
		this.neighbours[2] = ck;
	}
	public void setWestNeighbours(ModelTileCK ck)
	{
		this.neighbours[3] = ck;
	}

	public String getLink()
	{
		return this.link;
	}

	public ModelConstraint[] getConstraints()
	{
		return super.constraints;
	}

	public ModelTile[] getNeighbours()
	{
		return super.neighbours;
	}

	public void setOwnerFirstTile(ModelPlayerCK mp)
	{
		for(int i = 0; i <9 ; i++)
		{
			this.getConstraints()[i].getPlayers().add(mp);
		}
	}

	/**
	 * Cette fonction nous permet de v�rifier qu'une tuile est bien posable.
	 * Il nous faut donc verifier qu'a chacun des voisins les contraintes correspondent.
	 */
	public boolean canPutTile(ModelTile[] neighbours) {

		boolean north = true , east= true , south= true , west= true ;


		ModelTileCK neighbourNorth = (ModelTileCK) neighbours[0];
		ModelTileCK neighbourEast =  (ModelTileCK) neighbours[1];
		ModelTileCK neighbourSouth =  (ModelTileCK)neighbours[2];
		ModelTileCK neighbourWest = (ModelTileCK) neighbours[3];


		if(neighbourNorth != null)
		{
			System.out.println("Constraints sud du voisin nord "+neighbourNorth.getSouthConstraints().toString());
			if(!neighbourNorth.getSouthConstraints().getName().equals(this.getNorthConstraints().getName()))
			{
				north = false;
				System.out.println("le voisin du nord ne correspond pas");
			}
		}

		if(neighbourEast != null)
		{
			System.out.println("Constraints west du voisin est "+neighbourEast.getWestConstraints().toString());
			if(!neighbourEast.getWestConstraints().getName().equals(this.getEastConstraints().getName()))
			{
				east = false;
				System.out.println("la contrainte est ne correspond pas");
			}
		}
		if(neighbourSouth != null)
		{System.out.println("Constraints nord du voisin sud "+neighbourSouth.getNorthConstraints().toString());
		if(!neighbourSouth.getNorthConstraints().getName().equals(this.getSouthConstraints().getName()))
		{
			south = false;
			System.out.println("le voisin sud ne correspond pas");
		}
		}
		if(neighbourWest != null)
		{
			System.out.println("Constraints est du voisin west "+neighbourWest.getEastConstraints().toString());
			if(!neighbourWest.getEastConstraints().getName().equals(this.getWestConstraints().getName()))
			{
				north = false;
				System.out.println("le voisin west ne correspond pas");
			}

		}	
		if(neighbourWest == null && neighbourSouth == null && neighbourEast == null && neighbourNorth == null)
			return false;

		if( north && east && south && west)
		{
			ModelTileCK list[] = {neighbourNorth,neighbourEast,neighbourSouth,neighbourWest};
			fillNeighbour(list);


		}
		return  north && east && south && west;
	}

	public ModelTileCK getNgh(int index)
	{
		return (ModelTileCK) super.neighbours[index];
	}
	
	public void fillNeighbour(ModelTileCK[] nghbList)
	{
		ModelTileCK neighbourNorth = nghbList[0];
		ModelTileCK neighbourEast = nghbList[1];
		ModelTileCK neighbourSouth = nghbList[2];
		ModelTileCK neighbourWest = nghbList[3];
		
		this.getNeighbours()[Constants.NGB_NORTH] = neighbourNorth;
		if(neighbourNorth != null)
		{
			//on lui designe moi comme voisin du sud
			neighbourNorth.setSouthNeighbours(this);
			this.setCstNFull(neighbourNorth.getSouthConstraintsFull());
		}


		this.getNeighbours()[Constants.NGB_EAST] = neighbourEast;
		if(neighbourEast != null)
		{
			//on lui designe moi comme voisin du west
			neighbourEast.setWestNeighbours(this);
			this.setCstEFull(neighbourEast.getWestConstraintsFull());

		}

		this.getNeighbours()[Constants.NGB_SOUTH] = neighbourSouth;
		if(neighbourSouth != null)
		{
			//on lui designe moi comme voisin du nord
			neighbourSouth.setNorthNeighbours(this);
			this.setCstSFull(neighbourSouth.getNorthConstraintsFull());
		}

		this.getNeighbours()[Constants.NGB_WEST] = neighbourWest;
		if(neighbourWest != null)
		{
			//on lui designe moi comme voisin de l'est
			neighbourWest.setEastNeighbours(this);
			this.setCstWFull(neighbourWest.getEastConstraintsFull());
		}

	}

	public void setPicture(Image picture) {
		this.picture = picture;
	}

	public Image getPicture() {
		return picture;
	}
	public void angleRepaint() throws IOException
	{
		
		AffineTransform tx = new AffineTransform();
		tx.rotate(Math.toRadians(this.newAngle), this.picture.getWidth(null) / 2, this.picture.getHeight(null) / 2);
		AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
		op = new AffineTransformOp(tx,AffineTransformOp.TYPE_BILINEAR);
		this.picture = op.filter((BufferedImage) this.picture, op.createCompatibleDestImage((BufferedImage) this.picture, null) );
		
		// on n'oublie pas de maj le old angle
		this.setOldAngle(this.newAngle);
	}
	
}
